/*
 * File:   RoutingTable.cpp
 * Author: henrya
 *
 * Created on June 26, 2009, 6:54 PM
 */

#include "RoutingTable.h"
#include "RuntimeException.h"
#include <limits>
#include <sstream>
using std::numeric_limits;
using std::stringstream;
using std::endl;

RoutingTable::RoutingTable(int nodeId, vector<int>* allNodes) : nodeId(nodeId), allNodes(*allNodes), version(0) {
    distance = new int[allNodes->size()];
    if (distance == NULL) {
        RuntimeException ex("RoutingTable - Could not allocate memory for ditance vector");
        throw ex;
    }
    bool valid(false);
    int maxInt = numeric_limits<int>::max() - 1;
    for (unsigned int i = 0; i < allNodes->size(); i++) {
        if (indexOf(nodeId) == i) {
            distance[i] = 0;
            valid = true;
        } else {
            distance[i] = maxInt;
        }
    }
    if (!valid) {
        RuntimeException ex("RoutingTable - nodeId not in allNodes vector!");
        throw ex;
    }
    nextNode = new int[allNodes->size()];
    if (nextNode == NULL) {
        RuntimeException ex("RoutingTable - Could not allocate memory for next node vector");
        throw ex;
    }
    for (unsigned int i = 0; i < allNodes->size(); i++) {
        if (indexOf(nodeId) == i) {
            nextNode[i] = i;
        } else {
            nextNode[i] = -1;
        }

    }
    lastVersion = new unsigned int[allNodes->size()];
    if (lastVersion == NULL) {
        RuntimeException ex("RoutingTable - Could not allocate memory for last version vector");
        throw ex;
    }
    for (unsigned int i = 0; i < allNodes->size(); i++) {
        lastVersion[i] = 0;
    }
}

RoutingTable::~RoutingTable() {
    delete distance;
    delete nextNode;
    delete lastVersion;
}

bool RoutingTable::update(RoutingTable& table) {
    bool res = false;
    if (lastVersion[indexOf(table.getId())] < table.getVersion()) {
        lastVersion[indexOf(table.getId())] = table.getVersion();
        for (unsigned int i = 0; i < allNodes.size(); i++) {
            int newDistance(table.distance[i] + 1);
            if ((i != indexOf(nodeId)) && (distance[i] > newDistance)) {
                distance[i] = newDistance;
                nextNode[i] = indexOf(table.getId());
                res = true;
            }
        }
    }
    return res;
}

unsigned int RoutingTable::indexOf(int nodeNumber) {
    for (unsigned int i = 0; i < allNodes.size(); i++) {
        if (nodeNumber == allNodes.at(i)) {
            return i;
        }
    }
    return -1;
}

int RoutingTable::getId() {
    return nodeId;
}

string* RoutingTable::printRT() {
    stringstream res;
    res << "Routing Table for Node " << nodeId << " :" << endl;
    res << "Target:\tDistance:\tNext:\n";
    for (vector<int>::iterator it = allNodes.begin(); it != allNodes.end(); it++) {
        res << *it << "\t";
        int dist = distance[indexOf(*it)];
        if (dist == numeric_limits<int>::max() - 1) {
            res << "infinite";
        } else {
            res << dist << "\t";
        }

        res << "\t";
        int next = nextNode[indexOf(*it)];
        if (next == -1) {
            res << "NA";
        } else {
            res << getNodeNumber(next);
        }
        res << endl;
    }
    res << endl;
    return new string(res.str());
}

int RoutingTable::getNodeNumber(unsigned int index) {
    return allNodes.at(index);
}

RoutingTable::RoutingTable(const RoutingTable* orig) : nodeId(orig->nodeId), allNodes(orig->allNodes),version(orig->version) {
    distance = new int[allNodes.size()];
    if (distance == NULL) {
        RuntimeException ex("RoutingTable - Could not allocate memory for ditance vector");
        throw ex;
    }
    for (unsigned int i = 0; i < allNodes.size(); i++) {
        distance[i] = orig->distance[i];
    }
    nextNode = new int[allNodes.size()];
    if (nextNode == NULL) {
        RuntimeException ex("RoutingTable - Could not allocate memory for next node vector");
        throw ex;
    }
    for (unsigned int i = 0; i < allNodes.size(); i++) {
        nextNode[i] = orig->nextNode[i];
    }
}

void RoutingTable::setVersion(unsigned int version) {
    this->version=version;
}
unsigned int RoutingTable::getVersion() {
    return version;
}

int RoutingTable::getNextOnPath(int dest){
    return getNodeNumber(nextNode[indexOf(dest)]);
}
